﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ExpressionLanguage;

namespace Example3
{
    class Program
    {
        public struct Vector4
        {
            public double _1;
            public double _2;
            public double _3;
            public double _4;
        }

        // This the registered function that can be called inside of the script.
        public static bool Vector4_Create(List<ExpressionInterpreter.ExecutionStack.Variable> parameters, out ExpressionInterpreter.ExecutionStack.Variable result)
        {
            result = ExpressionInterpreter.ExecutionStack.Variable.MakeVoid();
            // 1. First we check how many parameters we have in case the caller push too many parameters.
            // The parameter is pushed from left to right , so the left most parameter is associated with index 0.
            if (parameters.Count != 4)
                return false;
            // 2. Do what you wish to do.
            Vector4 vector4 = new Vector4();
            if (!parameters[0].AsNumber(out vector4._1))
                return false;
            if (!parameters[1].AsNumber(out vector4._2))
                return false;
            if (!parameters[2].AsNumber(out vector4._3))
                return false;
            if (!parameters[3].AsNumber(out vector4._4))
                return false;
            // 3. After populate user defined data, what you need to do is casting this data to object and
            // assign it result parameter. That's everything you need to do. We are done !
            result = new ExpressionInterpreter.ExecutionStack.Variable((object)(vector4));
            return true;
        }

        public static bool Vector4_Mul(List<ExpressionInterpreter.ExecutionStack.Variable> parameters, out ExpressionInterpreter.ExecutionStack.Variable result)
        {

            result = ExpressionInterpreter.ExecutionStack.Variable.MakeVoid();
            if (parameters.Count != 2)
                return false;
            Vector4 vector4 = (Vector4)(parameters[0].AsObject());
            double num;
            if (!parameters[1].AsNumber(out num))
                return false;
            vector4._1 *= num;
            vector4._2 *= num;
            vector4._3 *= num;
            vector4._4 *= num;
            result = new ExpressionInterpreter.ExecutionStack.Variable((object)(vector4));
            return true;
        }

        static void Main(string[] args)
        {
            // Suppose your game engine needs some way to initialize structure data, like vector4 which needs 4 double value components.
            // What you gonna  do ? This expression language doesn't support it! But don't be panic, you can extend it to support it.
            // I have provide a data type called UserData which is totally transparently to script runtime but you can grab it and populate
            // it or call function and make it as parameter. 

            // 1. Initialize Everything
            ExpressionInterpreter.ExecutionStack stack = new ExpressionInterpreter.ExecutionStack();
            ExpressionInterpreter.GlobalFunction func = new ExpressionInterpreter.GlobalFunction();
            CoreLibrary.LoadLibrary(func, stack);
            MathLibrary.LoadLibrary(func, stack);
            StringLibrary.LoadLibrary(func, stack);

            // 2. Since we will use our own function to initialize it and manipulate it, we need to register our own function. 
            // Register function is pretty simple, right ? One line one function.
            func.RegisterGlobalFunction("Vector4", Program.Vector4_Create);
            func.RegisterGlobalFunction("Vector4Mul", Program.Vector4_Mul);

            // 3. Write the configuration script here
            string script = "Vec1= Vector4(1.0,2.0,3.0,4.0);" +
                "Vec2=Vector4(2.0,3.0,4.0,5.0);" +
                "Vec3=Vector4Mul(Vector4(1.0,1.0,1.0,1.0),4.0);";

            // 4. Execute everything
            ErrorCollector error = ExpressionInterpreter.Execute(script, func, stack);
            if (!error.Success)
                return; // kidding me again.

            Vector4 Vec3 = (Vector4)(stack.LookUpAsObject("Vec3"));
            Console.WriteLine("Vec3={0},{1},{2},{3}", Vec3._1, Vec3._2, Vec3._3, Vec3._4);
            
            return;
        }
    }
}
